import numpy as np


class Perceptron(object):
    def __init__(self, a):
        self.a = a

    def train(self, samples, lr=1e-2):
        d = self.a.shape[0]

        stop_count = 0
        best_loss = np.inf
        best_a = self.a

        while True:
            gradient = np.zeros(d)
            for y in samples:
                score = np.dot(self.a, y)
                if score < 0:
                    gradient += y

            loss = np.linalg.norm(lr * gradient)
            # print(loss)
            if loss < best_loss:
                stop_count = 0
                best_loss = loss
                best_a = self.a
            else:
                stop_count += 1

            if stop_count == 5:
                self.a = best_a
                break

            self.a = self.a + lr * gradient

    def classify(self, sample):
        # r = g(x) / |w|
        return np.dot(self.a, sample) / np.linalg.norm(self.a[:-1])


if __name__ == '__main__':
    samples = np.array([
        [1, 1, 1],
        [2, 2, 1],
        [2, 0, 1],
        [0, 0, -1],
        [-1, 0, -1],
        [0, -1, -1]
    ])

    perceptron = Perceptron(np.array([1, 1, 0]))
    perceptron.train(samples, lr=0.1)
    print(perceptron.a)
    for y in samples:
        print(perceptron.classify(y))
